//
// Created by edward on 23-2-15.
//

#include "Queue_v3.h"
#include "LockFreeQueue_v0.h"
#include "utils.h"

#include <glog/logging.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <atomic>
#include <tbb/concurrent_queue.h>
#include <functional>

int kMaxThread = 2;
//const int kMaxThread = 32;
constexpr int kMax = 1000000;
constexpr int kMaxCheck = 10;

double use_time;
edward::Timer timer;

class Starter {
    std::mutex mtx_;
    bool flag_ = false;
    std::condition_variable cond_;
public:
    void wait() {
        std::unique_lock lk(mtx_);
        cond_.wait(lk, [this](){
            return flag_;
        });
    }
    void start() {
        std::lock_guard lk(mtx_);
        flag_ = true;
        cond_.notify_all();
    }

    void init() {
        flag_ = false;
    }

};




bool check() {
    Starter starter;
    std::atomic<int> cnt = 0;
//edward::multithread::Queue<int> queue;
    edward::multithread::LockFreeQueue<int> queue;
//tbb::concurrent_queue<int> queue;

    std::function<void(int, int)> produce = [&](int l, int r) {
        starter.wait();
//    edward::print("producer start");
        while (l < r) {
//        edward::print("produce ->", l);
            queue.push(l++);
        }
    };

    std::function<void(std::vector<int>&)> consume = [&](std::vector<int> &popList) {
        starter.wait();
//    edward::print("consumer start");
        std::shared_ptr<int> p;
//    int t;
        while (cnt.load() < kMax) {
            while (!(p = queue.tryPop())) {
                std::this_thread::yield();  //自旋
                if (cnt.load() >= kMax) return;
//            edward::print("unvalid pointer");
            }
            popList.push_back(*p);
            ++cnt;
//        p = queue.pop();
//        edward::print("consume", *p);
            /*
            int t;
            while (queue.try_pop(t)) {
                popList.push_back(t);
//                LOG(INFO) << "consume " << t;
                ++cnt;
            }
            */
        }
    };

    std::vector<std::thread> threads;

    int produceNum = kMaxThread / 2, consumeNum = kMaxThread - produceNum;
//    int produceNum = 3, consumeNum = 1;
    int idx = 0, step = kMax / produceNum;


    for (int i = 1; i < produceNum; ++i) {
        threads.emplace_back(produce, idx, idx + step);
        idx += step;
    }
    threads.emplace_back(produce, idx, kMax);

    std::vector<std::vector<int>> popLists(consumeNum);
    for (int i = 0; i < consumeNum; ++i) {
        threads.emplace_back(consume, std::ref(popLists[i]));
    }

//    std::this_thread::sleep_for(std::chrono::seconds(1));
    timer.reset();
    starter.start();
    for (auto &&t : threads) {
        t.join();
    }
    use_time += timer.count();

    std::vector<int> checkList;
    checkList.reserve(kMax);
    for (auto &&popList : popLists) {
        for (auto &&x : popList) {
            checkList.push_back(x);
        }
    }

    std::sort(checkList.begin(), checkList.end());

    bool flag = true;
    for (int i = 0; i < kMax; ++i) {
        if (checkList[i] != i) {
            flag = false;
            break;
        }
    }

    return flag;
}

int main(int argc, char *argv[]) {
//    google::InitGoogleLogging(argv[0]);  // 初始化 glog

    for (; kMaxThread <= 8; ++kMaxThread) {
        use_time = 0;
        for (int i = 0; i < kMaxCheck; ++i) {
            check();
        }
        std::cout << use_time / kMaxCheck << ", ";
    }
    std::cout << std::endl;

    return 0;

}